#include <iostream>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include <cassert>
#include <vector>

#include "client.h"
#include "fountain.h"
#include "gfx.h"
#include "highground.h"
#include "map.h"
#include "object.h"
#include "objects.h"
#include "spells.h"
#include "sfont.h"
#include "textbox.h"
#include "wizard.h"
#include "ui.h"
#include "minimap.h"
#include "effects.h"

extern SDL_Surface* screen;
extern SDL_Surface* windows;
SFont_Font* font;
Map* map;
Effects* effects;
//Window* window;

//Object **objects = NULL;
//Wizard **wizards = NULL;

//unsigned int nObjects = 0;

unsigned int xRes = 800;
unsigned int yRes = 600;
unsigned int bpp = 32;
unsigned int windowX = 0;
unsigned int windowY = 0;
unsigned int windowW = 25;
unsigned int windowH = 18;

bool inputLocked = false;
Uint8* keys;
Uint8 mouseButtons;
signed int mouseX, mouseY;
Client* client;
Wizard* myWizard;
Objects objects;

UI::Window* miniMapWindow;
MiniMap* miniMapImage;
UI::TextBox* console;

unsigned int phase;
unsigned int round;

extern unsigned char screenFilterAlpha;
extern SDL_Surface *screenFilter;

int main(int argc, char *argv[]);
void drawObjects();
void drawScreen();
void lockInput();
void unlockInput();
void toggleInputLock();
void handleMovement(unsigned x, unsigned y);

UI::UI* ui;

int main(int argc, char *argv[]) {
	initSDL(xRes, yRes, bpp);
	initFonts();
	initUI();
	
	SDL_EnableUNICODE(SDL_ENABLE);
	
	map = new Map();
	phase = PHASE_MOVEMENT;
	round = 1;
	
	ui = new UI::UI();
	
	//messages = new TextBox(2);
	//messages->setFont(font);
	//messages->write("Highground Started");
	
	UI::InputBox* inputBox;
	//UI::Button* button;
	UI::Window* window;
	//UI::Image* image;
	
	window = new UI::Window(132, yRes - 170, 400, 145, "Console");
	console = new UI::TextBox("Welcome to highground\n", 0, 15, 400, 100, 12);
	//textBox->addText("\nThis is some added text\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10");
	//textBox->setColour(255,0,0);
	UI::ChatInputBoxEvent textEvent;
	inputBox = new UI::InputBox(5, 120, 390, 20, 12, 256, &textEvent);
	UI::TestEvent testEvent;
	//button = new UI::Button(50, 300, 100, 25, "Click me", 12, &testEvent);
	
	window->addTextBox(console);
	window->addInputBox(inputBox);
	//window->addButton(button);
	
	ui->addWindow(window);
	
	//window = new UI::Window(50, 50, 50, 50, "test");
	//image = new UI::Image(0, 16, 0, 0, "images/wiz_south.bmp");
	//window->addImage(image);
	//ui->addWindow(window);
	
	miniMapImage = new MiniMap(1, 15, 0, 0);
	miniMapWindow = new UI::Window(0, yRes-170, 130, 145, "Minimap");
	miniMapWindow->addImage(miniMapImage);
	ui->addWindow(miniMapWindow);
	
	SDL_Color tintColour;
	tintColour.r = 127;
	tintColour.g = 0;
	tintColour.b = 0;
	effects = new Effects();
	//effects->addTileTint(2, 2, &tintColour, 64);
	//effects->addTileTint(3, 2, &tintColour, 64);
	//effects->addTileTint(4, 2, &tintColour, 64);
	//effects->addTileTint(3, 1, &tintColour, 64);
	//effects->addTileTint(3, 3, &tintColour, 64);
	
	std::cout << "hgclient: initialising SDLNet" << std::endl;
	SDLNet_Init();
	atexit(SDLNet_Quit);
	std::cout << "hgclient: connecting to server" << std::endl;
	client = new Client();
	if (client->connect("localhost" /* "cremoris.ath.cx" */, 0x1234) == 0) {
		std::cout << "Connection to server failed.\n";
		console->addText("Failed to connect to server\n");
	} else {
		console->addText("Connected to server\n");
	}
	std::cout << "hgclient: awaiting input" << std::endl;
	
	SDL_Event event;
	
	unsigned int frameTicks = 0;
	unsigned int objectTicks = 0;
	unsigned int scrollTicks = 0;
	unsigned int waterFrameTicks = 0;
	
	// lockInput();
	
	srand(time(NULL));
	
	unsigned int testTicks = 0;
	
	while (1) {
		// every second, test various features
		if (SDL_GetTicks() - testTicks > 1000) {
			// buildHill(rand() % map->getWidth(), rand() % map->getHeight(), 32, 64);
			// digHole(rand() % map->getWidth(), rand() % map->getHeight(), 32, 64);
			effects->clearTileTints();
			for (unsigned int i = 0; i < objects.getNWizards(); ++i) {
				if (objects.getWizard(i)->getPathSize() == 0) {
					objects.getWizard(i)->calculatePaths();
				}
				for (unsigned int x = 0; x < map->getWidth(); ++x) {
					for (unsigned int y = 0; y < map->getHeight(); ++y) {
						if (objects.getWizard(i)->getIsReachable(x, y)) {
							tintColour.r = 127;
							tintColour.g = 0;
							tintColour.b = 0;
							effects->addTileTint(x, y, &tintColour, 63);
						}
					}
				}
			}
			testTicks = SDL_GetTicks();
		}
		
		// Every 25ms, call tick() on all the objects.
		if (SDL_GetTicks() - objectTicks > 25) {
			for (unsigned int i = 0; i < objects.getNObjects(); ++i) {
				objects.getObject(i)->tick();
			}
			ui->tick();
			objectTicks = SDL_GetTicks();
		}
		// Every 25ms, redraw the screen.
		if (SDL_GetTicks() - frameTicks > 25) {
			map->drawEarth();
			drawObjects();
			map->drawWater();
			drawScreen();
			frameTicks = SDL_GetTicks();
		}
		
		// Every 50ms, change the water frame
		if (SDL_GetTicks() - waterFrameTicks > 50) {
			map->nextFrame();
			waterFrameTicks = SDL_GetTicks();
		}
		
		while (SDL_PollEvent(&event)) {
			switch (event.type) {
				case SDL_QUIT:
					exit(0);
					break;
				case SDL_KEYDOWN:
				case SDL_KEYUP:
					ui->keyEvent(event.key);
					if(event.key.keysym.sym == SDLK_ESCAPE)
						exit(0);
					break;
				case SDL_MOUSEMOTION:
					ui->mouseMotionEvent(event.motion);
					break;
				case SDL_MOUSEBUTTONUP:
				case SDL_MOUSEBUTTONDOWN:
					if (ui->mouseButtonEvent(event.button) == false) {
						// GAME CLICK CODE HERE. false is returned when click
						// wasnt on a window or whatever =)
						unsigned int x = ((event.button.x / SPRITE_WIDTH) +
						                  windowX);
						unsigned int y = ((event.button.y / SPRITE_HEIGHT) +
						                  windowY);
						tintColour.r = 0;
						tintColour.g = 0;
						tintColour.b = 127;
						effects->addTileTint(x, y, &tintColour, 127);
						if (phase == PHASE_MOVEMENT)
							handleMovement(x, y);
					}
					break;
			}
		}
		
		// Save mouse buttons state and cursor position
		mouseButtons = SDL_GetMouseState(&mouseX, &mouseY);
		
		// If the middle mouse button has been pressed, toggle the mouse and
		// keyboard lock.
		if (mouseButtons & SDL_BUTTON(2))
			toggleInputLock();
		// If any other mouse button has been prssed, lock the mouse
		// and keyboard.
		//else if ((mouseButtons & SDL_BUTTON(1)) || (mouseButtons & SDL_BUTTON(3)))
		//	lockInput();
		
		// Every 50ms, check if the mouse is at the edge of the screen and
		// if so, scroll the map.
		if (SDL_GetTicks() - scrollTicks > 50) {
			if ((xRes - mouseX < 10) && (windowX + windowW < map->getWidth()))
				++windowX;
			if ((mouseX < 10) && (windowX > 0))
				--windowX;
			if ((yRes - mouseY < 10) && (windowY + windowH < map->getHeight()))
				++windowY;
			if ((mouseY < 10) && (windowY > 0))
				--windowY;
			scrollTicks = SDL_GetTicks();
		}
		
		client->getMessages();
		
		SDL_Delay(1);
	}
	return EXIT_SUCCESS;
}

void handleMovement(unsigned x, unsigned y) {
	for (unsigned i = 0; i < objects.getNWizards(); ++i) {
		if ((objects.getWizard(i)->getClientId() == client->getMe()) 
				&& (objects.getWizard(i)->getIsMyTurn())) {
			if ((objects.getWizard(i)->getPathSize() == 0) &&
				(objects.getWizard(i)->getIsReachable(x, y))) {
				objects.getWizard(i)->setPath(x, y);
				client->sendMove(x, y);
			}
		}
	}
}

void drawObjects() {
	unsigned int i;
	for (i = 0; i < objects.getNObjects(); ++i) {
		objects.getObject(i)->draw();
	}
}

void drawScreen() {
	if (screenFilter != NULL) {
		drawSurface(screenFilter, 0, 0, screenFilterAlpha);
	}
	effects->blit(screen);
	ui->blit(screen);
	SDL_UpdateRect(screen, 0, 0, 0, 0);
}

// This function will lock the mouse and keystrokes.
void lockInput() {
	if (inputLocked == true) {
			return;
	} else if (SDL_WM_GrabInput(SDL_GRAB_ON) != SDL_GRAB_ON) {
		std::cout << "Could not grab mouse and keyboard input..." << std::endl;
		inputLocked = false;
	} else {
		inputLocked = true;
	}
}

// This function will unlock the mouse and keystrokes.
void unlockInput() {
	if (inputLocked == false) {
			return;
	} else if (SDL_WM_GrabInput(SDL_GRAB_OFF) != SDL_GRAB_OFF) {
		// This should really never happen...
		std::cout << "Could not ungrab mouse and keyboard input..."
		          << std::endl;
		inputLocked = true;
	} else {
		inputLocked = false;
	}
}

void toggleInputLock() {
	if (inputLocked == true)
		unlockInput();
	else
		lockInput();
}

